package com.example.gateway.filter.factory;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;

/**
 * @title: RequestTimeGatewayFilterFactory
 * @description: 局部过滤器，用于记录请求处理时长，在yml中配置
 * @author: brucewong
 * @version: 1.0
 * @date：2019/10/27 16:43
 */
@Slf4j
public class RequestTimeGatewayFilterFactory extends AbstractGatewayFilterFactory<RequestTimeGatewayFilterFactory.Config> {
    private static String REQUEST_TIME_BEGIN = "beginTime";

    public RequestTimeGatewayFilterFactory() {
        super(RequestTimeGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("withParams");
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            exchange.getAttributes().put(REQUEST_TIME_BEGIN, System.currentTimeMillis());
            return chain.filter(exchange).then(
                Mono.fromRunnable(() -> {
                    Long startTime = exchange.getAttribute(REQUEST_TIME_BEGIN);
                    if (startTime != null) {
                        StringBuilder sb = new StringBuilder(exchange.getRequest().getURI().getRawPath())
                                .append(": ")
                                .append(System.currentTimeMillis() - startTime)
                                .append("ms");
                        if (config.isWithParams()) {
                            sb.append(" params:").append(exchange.getRequest().getQueryParams());
                        }
                        log.info(sb.toString());
                    }
                })
            );
        };
    }

    public static class Config {
        private Boolean withParams;
        public Config() {
        }
        public void setWithParams(Boolean withParams) {
            this.withParams = withParams;
        }
        public boolean isWithParams() {
            return withParams;
        }
    }
}